﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Collections;
using NUnit.Framework;
using EnumUtils;
using EnumUtils.Extensions;
using EnumUtilsTests.TestingEnums;

namespace EnumUtilsTests.Extensions
{
  [TestFixture]
  public class FlagEnumExtensionsTests
  {
    #region Setup and Teardown
    #endregion



    #region BitSheftLeft Tests
//TODO: Make sure all bit-shifting tests are C.O.R.R.E.C.T.
    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    BitShiftLeftUsingRegularEnumTest( ) {
      Enum regularEnum = RegularEnum.f1;
      regularEnum.BitShiftLeft(3);
      Assert.Fail("Passing a regular enum should have failed, but it didn't");
    }


    [Test]
    public void
    BitShiftLeftUsingFlagsEnumTest( ) {
      Tuple<Enum, Enum, int>[] testingEnumsAndShiftValues = {
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum.f3, GoodFlagEnum.f6, 3),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum.f3, GoodFlagEnum.None, 4),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum.f3, GoodFlagEnum.None, 5),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f2 | GoodFlagEnum_WithPreDefinedFlagCombos.f4 | GoodFlagEnum_WithPreDefinedFlagCombos.f6, 1),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f3 | GoodFlagEnum_WithPreDefinedFlagCombos.f5 | GoodFlagEnum_WithPreDefinedFlagCombos.None, 2),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f4 | GoodFlagEnum_WithPreDefinedFlagCombos.f6 | GoodFlagEnum_WithPreDefinedFlagCombos.None, 3),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f5 | GoodFlagEnum_WithPreDefinedFlagCombos.None, 4),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f6 | GoodFlagEnum_WithPreDefinedFlagCombos.None, 5),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.None, 6),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f3, GoodFlagEnum_WithPreDefinedFlagCombos.f6, 3),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f3, GoodFlagEnum_WithPreDefinedFlagCombos.None, 4),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f3, GoodFlagEnum_WithPreDefinedFlagCombos.None, 5),
                                                        new Tuple<Enum, Enum, int>(BadFlagEnum_NotPowerOfTwoDivisible.f1, BadFlagEnum_NotPowerOfTwoDivisible.f2, 1),
                                                        new Tuple<Enum, Enum, int>(BadFlagEnum_NotPowerOfTwoDivisible.f1, BadFlagEnum_NotPowerOfTwoDivisible.f4, 2),
                                                        new Tuple<Enum, Enum, int>(BadFlagEnum_NotPowerOfTwoDivisible.f1, BadFlagEnum_NotPowerOfTwoDivisible.None, 3),
                                                        new Tuple<Enum, Enum, int>(BadFlagEnum_NotPowerOfTwoDivisible.f1, BadFlagEnum_NotPowerOfTwoDivisible.None, 4)
                                                      };

      foreach (Tuple<Enum,Enum, int> testDatum in testingEnumsAndShiftValues) {
        BitShiftLeftTestOnSpecificFlagEnum(testDatum.Item1, testDatum.Item3, testDatum.Item2);
      }
    }
    #endregion


    


    #region BitShiftRight Tests
    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    BitShiftRightUsingRegularEnumTest( ) {
      Enum regularEnum = RegularEnum.f3;
      regularEnum.BitShiftRight(3);
      Assert.Fail("Passing a regular enum should have failed, but it didn't");
    }


    [Test]
    public void
    BitShiftRightUsingFlagsEnumTest( ) {
      Tuple<Enum, Enum, int>[] testingEnumsAndShiftValues = {
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum.f5, GoodFlagEnum.f2, 3),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum.f5, GoodFlagEnum.f1, 4),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum.f5, GoodFlagEnum.None, 5),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.None | GoodFlagEnum_WithPreDefinedFlagCombos.f2 | GoodFlagEnum_WithPreDefinedFlagCombos.f4, 1),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f1 | GoodFlagEnum_WithPreDefinedFlagCombos.f3, 2),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.None | GoodFlagEnum_WithPreDefinedFlagCombos.f2, 3),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f1, 4),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.None, 5),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f5, GoodFlagEnum_WithPreDefinedFlagCombos.f2, 3),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f5, GoodFlagEnum_WithPreDefinedFlagCombos.f1, 4),
                                                        new Tuple<Enum, Enum, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f5, GoodFlagEnum_WithPreDefinedFlagCombos.None, 5),
                                                        new Tuple<Enum, Enum, int>(BadFlagEnum_NotPowerOfTwoDivisible.f4, BadFlagEnum_NotPowerOfTwoDivisible.f2, 1),
                                                        new Tuple<Enum, Enum, int>(BadFlagEnum_NotPowerOfTwoDivisible.f4, BadFlagEnum_NotPowerOfTwoDivisible.f1, 2),
                                                        new Tuple<Enum, Enum, int>(BadFlagEnum_NotPowerOfTwoDivisible.f4, BadFlagEnum_NotPowerOfTwoDivisible.None, 3),
                                                        new Tuple<Enum, Enum, int>(BadFlagEnum_NotPowerOfTwoDivisible.f4, BadFlagEnum_NotPowerOfTwoDivisible.None, 4)
                                                      };

      foreach (Tuple<Enum,Enum, int> testDatum in testingEnumsAndShiftValues) {
        BitShiftRightTestOnSpecificFlagEnum(testDatum.Item1, testDatum.Item3, testDatum.Item2);
      }
    }
    #endregion


    #region Helper Methods
    private void
    BitShiftLeftTestOnSpecificFlagEnum( Enum flagEnum, int shiftCount, Enum expectedEndingFlag ) {
      flagEnum = flagEnum.BitShiftLeft(shiftCount);
      System.Diagnostics.Debug.WriteLine("Expected: {0}\tActual: {1}", expectedEndingFlag, flagEnum);
      Assert.AreEqual(expectedEndingFlag, flagEnum);
    }


    private void
    BitShiftRightTestOnSpecificFlagEnum( Enum flagEnum, int shiftCount, Enum expectedEndingFlag ) {
      flagEnum = flagEnum.BitShiftRight(shiftCount);
      System.Diagnostics.Debug.WriteLine("Expected: {0}\tActual: {1}", expectedEndingFlag, flagEnum);
      Assert.AreEqual(expectedEndingFlag, flagEnum);
    }

    #endregion
  }
}
